﻿#include "as_channel.h"
#include "epoll_watch.h"
#include "thread_name.h"
#include "log.h"
#ifdef _WIN32
#else
#include <sys/epoll.h>
#endif
#include <iostream>


as::net::Channel::Channel(std::shared_ptr<EpollWatch> &ep, socket_fd fd)
	:ep_watch_(ep)
{
	socket_ = std::make_shared<Socket>(fd);
	LOG_DEBUG << "create channel 1, socket fd:" << fd;
}

as::net::Channel::Channel(std::shared_ptr<EpollWatch> &ep, std::shared_ptr<Socket> &socket)
	:ep_watch_(ep)
{
	socket_ = std::move(socket);

	LOG_DEBUG << "create channel 2, socket fd:" << socket_->get_fd();
}

as::net::Channel::~Channel()
{
	LOG_DEBUG << "del channel, socket fd:" << socket_->get_fd();
	ClearAllCoHandle();
}

void as::net::Channel::HandleEvent(TimeValue &t)
{
	LOG_DEBUG << "handle event, socket fd:" << socket_->get_fd();
	if (events_ & (EPOLLHUP | EPOLLRDHUP))
	{
		if (close_cb_)
		{
			close_cb_();
		}
	}

	if (events_ & (EPOLLIN | EPOLLPRI))
	{

		if (co_handles_[E_CO_READ])
		{
			co_handles_[E_CO_READ].resume();
		}
		else
		{
			if (read_cb_)
			{
				read_cb_(t);
			}
		}
	}

	if (events_ & EPOLLOUT)
	{
		if (write_cb_)
		{
			write_cb_();
		}

		if (co_handles_[E_CO_WRITE])
		{
			co_handles_[E_CO_WRITE].resume();
		}
	}

	if (events_ & (EPOLLPRI) && !(events_ & EPOLLIN))
	{
		if (close_cb_)
		{
			close_cb_();
		}

		if (co_handles_[E_CO_CLOSE])
		{
			co_handles_[E_CO_CLOSE].resume();
		}
	}

	if (events_ & (EPOLLERR))
	{
		if (err_cb_)
		{
			err_cb_();
		}

		if (co_handles_[E_CO_ERROR])
		{
			co_handles_[E_CO_ERROR].resume();
		}
	}
}

void as::net::Channel::SetEvents(int evs)
{
	events_ = evs;
}

int as::net::Channel::GetEvents()
{
	return events_;
}

void as::net::Channel::SetType(int type)
{
	type_ = type;
}

int as::net::Channel::GetType()
{
	return type_;
}

socket_fd as::net::Channel::GetFd()
{
	return socket_->get_fd();
}

std::shared_ptr<as::net::Socket>& as::net::Channel::GetSocket()
{
	return socket_;
}

void as::net::Channel::EnableReadAndWriteFlag()
{
	events_ |= (EPOLLIN | EPOLLPRI | EPOLLOUT);
	Update();
}

void as::net::Channel::EnableReadFlag()
{
	events_ |= (EPOLLIN | EPOLLPRI);
	Update();
}

void as::net::Channel::DisableReadFlag()
{
	events_ &= ~(EPOLLIN | EPOLLPRI);

	ClearCoHandle(E_CO_READ);
	Update();
}

void as::net::Channel::EnableWriteFlag()
{
	events_ |= EPOLLOUT;
	Update();
}

void as::net::Channel::DisableWriteFlag()
{
	events_ &= ~EPOLLOUT;

	ClearCoHandle(E_CO_WRITE);
	Update();
}

bool as::net::Channel::IsWriteFlag()
{
	return events_ & EPOLLOUT;
}

bool as::net::Channel::IsReadFlag()
{
	return events_ & EPOLLIN | EPOLLPRI;
}

bool as::net::Channel::HasFlag()
{
	return events_ == 0;
}

void as::net::Channel::ClearFlag()
{
	events_ = 0;
	ClearAllCoHandle();
	Update();
}

void as::net::Channel::RemoveLoop()
{
	if(ep_watch_.expired() == false)
		ep_watch_.lock()->RemoveChannel(this);
}

void as::net::Channel::Update()
{
	if(ep_watch_.expired() == false)
		ep_watch_.lock()->UpdateChannel(this);
}

void as::net::Channel::SetReadCoHandle(as::co::coroutine_handle handle)
{
	co_handles_[E_CO_READ] = handle;
}

void as::net::Channel::SetWriteCoHandle(as::co::coroutine_handle handle)
{
	co_handles_[E_CO_WRITE] = handle;
}

void as::net::Channel::SetCloseCoHandle(as::co::coroutine_handle handle)
{
	co_handles_[E_CO_CLOSE] = handle;
}

void as::net::Channel::SetErrorCoHandle(as::co::coroutine_handle handle)
{
	co_handles_[E_CO_ERROR] = handle;
}

void as::net::Channel::CoCallCallback(int co_type)
{
	TimeValue tv;
	switch (co_type)
	{
	case E_CO_READ:
	{
		if(read_cb_)
			read_cb_(tv);

		break;
	}
	case E_CO_WRITE:
	{
		if (write_cb_)
			write_cb_();

		break;
	}
	}
}

void as::net::Channel::ClearCoHandle(int co_type)
{
	auto &handle = co_handles_[co_type];
	if (handle)
	{
		handle.destroy();
		handle = nullptr;
	}
}

void as::net::Channel::ClearAllCoHandle()
{
	for (auto &handle : co_handles_)
	{
		if (handle)
		{
			handle.destroy();
			handle = nullptr;
		}
	}
}

void as::net::Channel::SetReadCallback(EventReadCb cb)
{
	read_cb_ = std::move(cb);
}

void as::net::Channel::SetWriteCallback(EventCb cb)
{
	write_cb_ = std::move(cb);
}

void as::net::Channel::SetErrorCallback(EventCb cb)
{
	err_cb_ = std::move(cb);
}

void as::net::Channel::SetCloseCallback(EventCb cb)
{
	close_cb_ = std::move(cb);
}

